package com.jzo2o.health.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.model.CurrentUserInfo;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.health.constant.TradeConstants;
import com.jzo2o.health.enums.OrderPayStatusEnum;
import com.jzo2o.health.enums.OrderStatusEnum;
import com.jzo2o.health.mapper.OrdersCancelledMapper;
import com.jzo2o.health.mapper.OrdersMapper;
import com.jzo2o.health.mapper.OrdersRefundMapper;
import com.jzo2o.health.model.UserThreadLocal;
import com.jzo2o.health.model.domain.*;
import com.jzo2o.health.model.dto.request.OrdersCancelReqDTO;
import com.jzo2o.health.model.dto.request.OrdersPageQueryReqDTO;
import com.jzo2o.health.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.health.model.dto.response.*;
import com.jzo2o.health.properties.TradeProperties;
import com.jzo2o.health.service.IMemberService;
import com.jzo2o.health.service.IOrdersService;
import com.jzo2o.health.service.IReservationSetting;
import com.jzo2o.health.service.ISetmealService;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.mysql.utils.PageHelperUtils;
import com.jzo2o.trade.model.domain.Trading;
import com.jzo2o.trade.service.TradingService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author itlxc
 * @since 2024-10-22
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersService{

    private final ISetmealService setmealService;

    private final IMemberService memberService;

    @Resource
    private NativePayApi nativePayApi;

    private final TradeProperties tradeProperties;

    @Resource
    private TradingApi tradingApi;

    private final IReservationSetting reservationSetting;

    private final OrdersCancelledMapper ordersCancelledMapper;

    private final OrdersRefundMapper ordersRefundMapper;

    private final OrdersMapper ordersMapper;




    /**
     * 用户端预约下单
     * @param placeOrderReqDTO
     * @return
     */
    @Override
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
        Long userId = UserThreadLocal.currentUserId();
        if (ObjectUtil.isNull(userId)) {
            throw new ForbiddenOperationException("请登录后再操作");
        }
        Member member = memberService.getById(userId);
        if (ObjectUtil.isNull(member)) {
            throw new ForbiddenOperationException("用户信息不存在，请注册后再操作");
        }

        // 查询套餐
        Setmeal setmeal = setmealService.getById(placeOrderReqDTO.getSetmealId());
        if (ObjectUtil.isNull(setmeal)) {
            throw new ForbiddenOperationException("套餐不能为空");
        }

        // 封装订单对象
        Orders orders = BeanUtil.copyProperties(placeOrderReqDTO, Orders.class);
        orders.setSetmealId(setmeal.getId());
        orders.setSetmealAge(setmeal.getAge());
        orders.setSetmealImg(setmeal.getImg());
        orders.setSetmealPrice(BigDecimal.valueOf(setmeal.getPrice()));
        orders.setSetmealRemark(setmeal.getRemark());
        orders.setSetmealSex(setmeal.getSex());
        orders.setSetmealName(setmeal.getName());
        orders.setOrderStatus(OrderStatusEnum.NO_PAY.getStatus());
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        orders.setMemberId(userId);
        orders.setMemberPhone(member.getPhone());
        orders.setSortBy(System.currentTimeMillis());
        orders.setCreateTime(LocalDateTime.now());
        orders.setUpdateTime(LocalDateTime.now());
        boolean flag = this.save(orders);
        if (!flag) {
            throw new ForbiddenOperationException("下单失败");
        }
        log.info("下单成功>>>>>订单号为：{}", orders.getId());
        return new PlaceOrderResDTO(orders.getId());
    }

    /**
     * 用户支付
     * @param id
     * @param tradingChannel
     * @return
     */
    @Override
    public OrdersPayResDTO pay(Long id, PayChannelEnum tradingChannel) {
        // 查询订单判断是否存在
        Orders orders = this.getById(id);
        if (ObjectUtil.isNull(orders)) {
            // 不存在直接结束
            throw new ForbiddenOperationException("订单不存在");
        }

        // 订单存在 判断是否已经支付
        if (ObjectUtil.equal(orders.getPayStatus(), OrderPayStatusEnum.PAY_SUCCESS.getStatus()) && ObjectUtil.isNotNull(orders.getTradingOrderNo())) {
            // 已支付 直接返回支付结果
            OrdersPayResDTO ordersPayResDTO = BeanUtil.copyProperties(orders, OrdersPayResDTO.class);
            ordersPayResDTO.setProductOrderNo(orders.getId());
            return ordersPayResDTO;
        }

        // 检测订单状态是否正常
        if (ObjectUtil.notEqual(orders.getPayStatus(), OrderStatusEnum.NO_PAY.getStatus())) {
            // 订单状态异常 直接结束
            log.info("订单状态异常，订单号为：{}", orders.getId());
            throw new CommonException("订单状态异常");
        }

        // 未支付 调用支付服务生成二维码
        NativePayReqDTO nativePayReqDTO = new NativePayReqDTO();
        Long enterpriseId = ObjectUtil.equal(tradingChannel.getValue(), PayChannelEnum.ALI_PAY.getValue()) ? tradeProperties.getAliEnterpriseId() : tradeProperties.getWechatEnterpriseId();
        nativePayReqDTO.setEnterpriseId(enterpriseId);
        nativePayReqDTO.setProductOrderNo(orders.getId());
        nativePayReqDTO.setTradingChannel(tradingChannel);
        nativePayReqDTO.setTradingAmount(orders.getSetmealPrice());
        nativePayReqDTO.setProductAppId(TradeConstants.PRODUCT_APP_ID);
        nativePayReqDTO.setMemo(orders.getSetmealName());

        // 如果在订单中有支付渠道并且和新传入支付渠道不一致，说明用户切换支付渠道
        if (ObjectUtil.isNotEmpty(orders.getTradingChannel()) && ObjectUtil.notEqual(orders.getTradingChannel(), tradingChannel.getValue())) {
            nativePayReqDTO.setChangeChannel(true);
        }
        NativePayResDTO payResDTO = nativePayApi.createDownLineTrading(nativePayReqDTO);
        log.info("生成支付二维码成功： {}", payResDTO.getQrCode());

        if (ObjectUtil.isNull(payResDTO)) {
            log.info("请求支付服务生成二维码，更新订单:{}的交易单号和支付渠道失败", id);
            throw new CommonException("请求支付服务生成二维码，更新订单:" + id + "的交易单号和支付渠道失败");
        }

        // 订单状态正常 更新订单数据
        orders.setTradingChannel(payResDTO.getTradingChannel());
        orders.setTradingOrderNo(payResDTO.getTradingOrderNo());
        orders.setUpdateTime(LocalDateTime.now());
        orders.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
        orders.setPayTime(LocalDateTime.now());
        orders.setOrderStatus(OrderStatusEnum.WAITING_CHECKUP.getStatus());
        orders.setUpdateTime(LocalDateTime.now());
        this.updateById(orders);

        // 返回支付结果
        OrdersPayResDTO ordersPayResDTO = BeanUtil.toBean(payResDTO, OrdersPayResDTO.class);
        ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
        return ordersPayResDTO;
    }

    /**
     * 获取支付结果
     * @param id
     * @return
     */
    @Override
    public OrdersPayResDTO getPayResultFromTradServer(Long id) {
        Orders orders = this.getById(id);
        if (ObjectUtil.isNull(orders)) {
            throw new ForbiddenOperationException("订单不存在");
        }
        TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());
        if (ObjectUtil.isNull(tradingResDTO)) {
            throw new CommonException("查询支付结果失败");
        }
        OrdersPayResDTO ordersPayResDTO = BeanUtil.copyProperties(tradingResDTO, OrdersPayResDTO.class);
        ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
        log.info("查询支付结果成功：{}", JSONUtil.toJsonStr(ordersPayResDTO));
        return ordersPayResDTO;
    }

    /**
     * 用户端取消未支付订单
     * @param ordersCancelReqDTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancel(OrdersCancelReqDTO ordersCancelReqDTO) {
        // 查询订单判断是否存在
        Orders orders = this.getById(ordersCancelReqDTO.getId());
        if (ObjectUtil.isNull(orders)) {
            // 不存在 直接结束
            throw new ForbiddenOperationException("订单不存在");
        }

        // 存在 判断是否已经取消过
        if (ObjectUtil.equal(orders.getOrderStatus(), OrderStatusEnum.CANCELLED.getStatus())) {
            // 如果已经取消 直接结束
            throw new ForbiddenOperationException("订单已取消过了，请忽重复操作");
        }

        // 未取消 检测订单状态
        if (ObjectUtil.notEqual(orders.getOrderStatus(), OrderStatusEnum.NO_PAY.getStatus())) {
            throw new ForbiddenOperationException("订单状态异常");
        }

        //  修改订单状态为已取消
        boolean flag = this.lambdaUpdate()
                .set(Orders::getOrderStatus, OrderStatusEnum.CANCELLED.getStatus())
                .set(Orders::getUpdateTime, LocalDateTime.now())
                .eq(Orders::getId, ordersCancelReqDTO.getId())
                .eq(Orders::getOrderStatus, OrderStatusEnum.NO_PAY.getStatus())
                .update();
        if (!flag) {
            throw new CommonException("取消订单失败");
        }

        // 需要取消预约
        reservationSetting.lambdaUpdate()
                .setSql("reservations = reservations - 1")
                .eq(ReservationSetting::getOrderDate, orders.getReservationDate())
                // 乐观锁
                .lt(ReservationSetting::getReservations, 0)
                .update();

        // 保存取消订单记录
        OrdersCancelled ordersCancelled = new OrdersCancelled();
        ordersCancelled.setId(orders.getId());
        ordersCancelled.setCancellerId(orders.getMemberId());
        ordersCancelled.setCancellerName(orders.getCheckupPersonName());
        ordersCancelled.setCancellerType(1);
        ordersCancelled.setCancelReason(ordersCancelReqDTO.getCancelReason());
        ordersCancelled.setCancelTime(LocalDateTime.now());
        ordersCancelled.setCreateTime(LocalDateTime.now());
        ordersCancelled.setUpdateTime(LocalDateTime.now());
        ordersCancelledMapper.insert(ordersCancelled);
    }

    /**
     * 用户端订单退款
     * @param ordersCancelReqDTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refund(OrdersCancelReqDTO ordersCancelReqDTO) {

        // 查询订单判断是否存在
        Orders orders = this.getById(ordersCancelReqDTO.getId());
        if (ObjectUtil.isNull(orders)) {
            // 订单不存在 直接结束
            throw new ForbiddenOperationException("订单不存在");
        }

        // 订单存在 判断是否已经取消过
        if (ObjectUtil.equal(orders.getOrderStatus(), OrderStatusEnum.CLOSED.getStatus())) {
            // 已经取消 直接结束
            throw new ForbiddenOperationException("订单已取消过了，请忽重复操作");
        }

        // 未取消 判断订单状态是否为已支付
        if (ObjectUtil.notEqual(orders.getPayStatus(), OrderPayStatusEnum.PAY_SUCCESS.getStatus())) {
            // 不是 直接结束
            throw new ForbiddenOperationException("订单状态异常");
        }

        // 更新订单状态为已关闭 支付状态为退款中
        boolean flag = this.lambdaUpdate()
                .set(Orders::getOrderStatus, OrderStatusEnum.CLOSED.getStatus())
                .set(Orders::getPayStatus, OrderPayStatusEnum.REFUNDING.getStatus())
                .set(Orders::getUpdateTime, LocalDateTime.now())
                .eq(Orders::getId, ordersCancelReqDTO.getId())
                .eq(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())
                .update();
        if (!flag) {
            throw new CommonException("退款失败");
        }
        log.info("用户端订单退款成功>>>>>交易单号为：{}", orders.getTradingOrderNo());

        // 取消预约
        reservationSetting.lambdaUpdate()
                .setSql("reservations = reservations - 1")
                .eq(ReservationSetting::getOrderDate, orders.getReservationDate())
                // 乐观锁
                .lt(ReservationSetting::getReservations, 0)
                .update();

        // 保存取消订单记录
        OrdersCancelled ordersCancelled = new OrdersCancelled();
        ordersCancelled.setId(orders.getId());
        ordersCancelled.setCancellerId(orders.getMemberId());
        ordersCancelled.setCancellerName(orders.getCheckupPersonName());
        ordersCancelled.setCancellerType(1);
        ordersCancelled.setCancelReason(ordersCancelReqDTO.getCancelReason());
        ordersCancelled.setCancelTime(LocalDateTime.now());
        ordersCancelled.setCreateTime(LocalDateTime.now());
        ordersCancelled.setUpdateTime(LocalDateTime.now());
        ordersCancelledMapper.insert(ordersCancelled);

        // 保存退款记录
        OrdersRefund ordersRefund = new OrdersRefund();
        ordersRefund.setId(orders.getId());
        ordersRefund.setTradingOrderNo(orders.getTradingOrderNo());
        ordersRefund.setRealPayAmount(orders.getSetmealPrice());
        ordersRefund.setCreateTime(LocalDateTime.now());
        ordersRefundMapper.insert(ordersRefund);
    }

    /**
     * 用户分页查询订单
     * @param ordersStatus
     * @param sortBy
     * @return
     */
    @Override
    public List<OrdersResDTO> pageQuery(Integer ordersStatus, Long sortBy) {
        Long userId = UserThreadLocal.currentUserId();
        if (ObjectUtil.isNull(userId)) {
            throw new ForbiddenOperationException("请登录后再操作");
        }
        List<Orders> ordersList = this.lambdaQuery()
                .eq(Orders::getMemberId, userId)
                .eq(ObjectUtil.isNotNull(ordersStatus), Orders::getOrderStatus, ordersStatus)
                .orderByDesc(ObjectUtil.isNotNull(sortBy), Orders::getSortBy)
                .list();

        return BeanUtil.copyToList(ordersList, OrdersResDTO.class);
    }

    /**
     * 统计订单数量
     * @return
     */
    @Override
    public OrdersCountResDTO countByStatus() {

        Map<String, Object> params = new HashMap<>();
        OrdersCountResDTO ordersCountResDTO = new OrdersCountResDTO();

        // 待支付数量
        params.put("orderStatus", OrderStatusEnum.NO_PAY);
        ordersCountResDTO.setNoPayCount(ordersMapper.countOrderByStatus(params));

        // 待体检数量
        params.put("orderStatus", OrderStatusEnum.WAITING_CHECKUP);
        ordersCountResDTO.setWaitingCheckupCount(ordersMapper.countOrderByStatus(params));

        // 已体检数量
        params.put("orderStatus", OrderStatusEnum.COMPLETED_CHECKUP);
        ordersCountResDTO.setCompletedCheckupCount(ordersMapper.countOrderByStatus(params));

        // 已关闭数量
        params.put("orderStatus", OrderStatusEnum.CLOSED);
        ordersCountResDTO.setClosedCount(ordersMapper.countOrderByStatus(params));

        // 已取消数量
        params.put("orderStatus", OrderStatusEnum.CANCELLED);
        ordersCountResDTO.setCancelledCount(ordersMapper.countOrderByStatus(params));

        // 全部数量
        ordersCountResDTO.setTotalCount(ordersMapper.selectCount(null));


        return ordersCountResDTO;
    }

    /**
     * 查询订单详情
     * @param id
     * @return
     */
    @Override
    public OrdersDetailResDTO detail(Long id) {
        Orders orders = this.getById(id);
        OrdersDetailResDTO ordersDetailResDTO = BeanUtil.copyProperties(orders, OrdersDetailResDTO.class);
        OrdersCancelled ordersCancelled = ordersCancelledMapper.selectById(id);
        if (ObjectUtil.isNotNull(ordersCancelled)) {
            ordersDetailResDTO.setCancelReason(ordersCancelled.getCancelReason());
            ordersDetailResDTO.setCancelTime(ordersCancelled.getCancelTime());
        }
        return ordersDetailResDTO;
    }

    /**
     * 管理端分页查询订单
     * @param ordersPageQueryReqDTO
     * @return
     */
    @Override
    public PageResult<OrdersResDTO> adminPageQuery(OrdersPageQueryReqDTO ordersPageQueryReqDTO) {
        PageResult<Orders> pageResult = PageHelperUtils.selectPage(ordersPageQueryReqDTO,
                () -> this.lambdaQuery()
                        .eq(ObjectUtil.isNotNull(ordersPageQueryReqDTO.getOrderStatus()), Orders::getOrderStatus, ordersPageQueryReqDTO.getOrderStatus())
                        .like(StrUtil.isNotBlank(ordersPageQueryReqDTO.getMemberPhone()), Orders::getMemberPhone, ordersPageQueryReqDTO.getMemberPhone())
                        .orderByDesc(Orders::getSortBy)
                        .list()
        );

        return PageResult.of(pageResult, OrdersResDTO.class);
    }

    /**
     * 管理端查询订单详情
     * @param id
     * @return
     */
    @Override
    public AdminOrdersDetailResDTO adminDetail(Long id) {
        Orders orders = this.getById(id);
        if (ObjectUtil.isNull(orders)) {
            return null;
        }

        // 订单信息
        AdminOrdersDetailResDTO.OrderInfo orderInfo = BeanUtil.copyProperties(orders, AdminOrdersDetailResDTO.OrderInfo.class);
        AdminOrdersDetailResDTO adminOrdersDetailResDTO = new AdminOrdersDetailResDTO();
        adminOrdersDetailResDTO.setOrderInfo(orderInfo);

        // 取消订单信息
        OrdersCancelled ordersCancelled = ordersCancelledMapper.selectById(id);
        AdminOrdersDetailResDTO.CancelInfo cancelInfo = BeanUtil.copyProperties(ordersCancelled, AdminOrdersDetailResDTO.CancelInfo.class);
        adminOrdersDetailResDTO.setCancelInfo(cancelInfo);

        // 支付信息
        AdminOrdersDetailResDTO.PayInfo payInfo = new AdminOrdersDetailResDTO.PayInfo();
        if (ObjectUtil.notEqual(orders.getPayStatus(), OrderPayStatusEnum.NO_PAY.getStatus())) {
            payInfo.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
        } else {
            payInfo.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        }
        payInfo.setPayTime(orders.getPayTime());
        payInfo.setTradingChannel(orders.getTradingChannel());
        payInfo.setThirdOrderId(orders.getTransactionId());
        adminOrdersDetailResDTO.setPayInfo(payInfo);

        // 退款信息
        if (ObjectUtil.notEqual(orders.getPayStatus(), OrderPayStatusEnum.NO_PAY.getStatus()) && ObjectUtil.notEqual(orders.getPayStatus(), OrderPayStatusEnum.PAY_SUCCESS.getStatus())) {
            AdminOrdersDetailResDTO.RefundInfo refundInfo = new AdminOrdersDetailResDTO.RefundInfo();
            refundInfo.setTradingChannel(orders.getTradingChannel());
            refundInfo.setRefundStatus(orders.getPayStatus());
            refundInfo.setRefundId(orders.getRefundId());
            if (ObjectUtil.isNotNull(ordersCancelled)) {
                refundInfo.setCancelReason(ordersCancelled.getCancelReason());
                refundInfo.setCancelTime(ordersCancelled.getCancelTime());
            }
            adminOrdersDetailResDTO.setRefundInfo(refundInfo);
        }


        return adminOrdersDetailResDTO;
    }
}
